package com.uziot.bucket.common.spring;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;

import java.io.FileInputStream;
import java.io.InputStream;
import java.util.*;
import java.util.Map.Entry;

public class SpringContainer {
    private static final Logger logger = LoggerFactory.getLogger(SpringContainer.class);

    public static final String SPRING_CONFIG = "dubbo.spring.config";

    public static final String ENVIRONMENT_FILE_NAME = "EnvironmentFile";

    public static final String PROPERTIES_FILE_NAME = "properties.file";


    static ClassPathXmlApplicationContext context;

    public static ClassPathXmlApplicationContext getContext() {
        return context;
    }

    public void start() {
        String configPath = System.getProperty(SPRING_CONFIG);

        context = new ClassPathXmlApplicationContext(configPath.split("[,\\s]+"), false);

        PropertySource<Map<String, Object>> galaxyPropertySource
                = new PropertiesPropertySource(ENVIRONMENT_FILE_NAME, parsePropertiesFile());
        context.getEnvironment().getPropertySources().addLast(galaxyPropertySource);

        context.refresh();
        context.start();
    }

    public void stop() {
        try {
            if (context != null) {
                context.stop();
                context.close();
                context = null;
            }
        } catch (Throwable e) {
            logger.error(e.getMessage(), e);
        }
    }

    protected Properties parsePropertiesFile() {
        String path = System.getProperty(PROPERTIES_FILE_NAME);
        if (path == null || path.length() == 0) {
            path = System.getenv(PROPERTIES_FILE_NAME);
        }
        Properties props = loadProperties(path);
        parseProperties(props);
        return props;
    }

    private void parseProperties(Properties props) {
        Map<String, String> files = new HashMap<>();
        for (Entry<Object, Object> entry : props.entrySet()) {
            String name = (String) entry.getKey();
            if (name.startsWith("import.")) {
                files.put(name.substring("import.".length()), (String) entry.getValue());
            }
        }
        for (Entry<String, String> entry : files.entrySet()) {
            String key = entry.getKey();
            String file = entry.getValue();
            logger.debug("import properties from file:{}[{}]", key, file);
            Properties tmp = loadProperties(file);
            parseProperties(tmp);
            props.putAll(tmp);
        }
    }

    public Properties loadProperties(String fileName) {
        Properties properties = new Properties();
        if (fileName.startsWith("/")) {
            try {
                try (FileInputStream input = new FileInputStream(fileName)) {
                    properties.load(input);
                }
            } catch (Throwable e) {
                logger.warn("Failed to load " + fileName + " file from " + fileName + "(ingore this file): " + e.getMessage(), e);
            }
            return properties;
        }

        checkMultiFile(fileName);
        try (InputStream is = ClassLoaderUtils.getClassLoader().getResourceAsStream(fileName)) {
            if (is == null) {
                logger.warn("No " + fileName + " found on the class path.");
            } else {
                properties.load(is);
            }
        } catch (Throwable e) {
            logger.warn("Failed to load " + fileName + " file from " + fileName + "(ingore this file): " + e.getMessage(), e);
        }
        return properties;
    }

    private void checkMultiFile(String fileName) {
        List<java.net.URL> list = new ArrayList<>();
        try {
            Enumeration<java.net.URL> urls = ClassLoaderUtils.getClassLoader().getResources(fileName);
            list = new ArrayList<>();
            while (urls.hasMoreElements()) {
                list.add(urls.nextElement());
            }
        } catch (Throwable t) {
            logger.warn("Fail to load " + fileName + " file: " + t.getMessage(), t);
        }
        if (list.size() > 1) {
            String errMsg = String.format("only 1 %s file is expected, but %d files found on class path: %s",
                    fileName, list.size(), list.toString());
            logger.warn(errMsg);
            throw new IllegalStateException(errMsg); // see http://code.alibabatech.com/jira/browse/DUBBO-133
        }
    }
}
